// 版权所有2011 Go作者。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package gc

import (
	"internal/race"
	"math/rand"
	"sort"
	"sync"

	"cmd/compile/internal/base"
	"cmd/compile/internal/ir"
	"cmd/compile/internal/liveness"
	"cmd/compile/internal/objw"
	"cmd/compile/internal/ssagen"
	"cmd/compile/internal/typecheck"
	"cmd/compile/internal/types"
	"cmd/compile/internal/walk"
	"cmd/internal/obj"
)

// “可移植”代码生成。

var (
	compilequeue []*ir.Func // 等待编译的函数
)

func enqueueFunc(fn *ir.Func) {
	if ir.CurFunc != nil {
		base.FatalfAt(fn.Pos(), "enqueueFunc %v inside %v", fn, ir.CurFunc)
	}

	if ir.FuncName(fn) == "_" {
		// 跳过编译空白函数。
		// 前端已报告任何规范规定的错误（#29870）。
		return
	}

	if clo := fn.OClosure; clo != nil && !ir.IsTrivialClosure(clo) {
		return // 我们将把它作为其封闭函数的一部分
	}

	if len(fn.Body) == 0 {
		// 如有必要，初始化ABI包装器。
		ssagen.InitLSym(fn, false)
		types.CalcSize(fn.Type())
		a := ssagen.AbiForBodylessFuncStackMap(fn)
		abiInfo := a.ABIAnalyzeFuncType(fn.Type().FuncType()) // abiInfo有包装器的溢出/主位置
		liveness.WriteFuncMap(fn, abiInfo)
		if fn.ABI == obj.ABI0 {
			x := ssagen.EmitArgInfo(fn, abiInfo)
			objw.Global(x, int32(len(x.P)), obj.RODATA|obj.LOCAL)
		}
		return
	}

	errorsBefore := base.Errors()

	todo := []*ir.Func{fn}
	for len(todo) > 0 {
		next := todo[len(todo)-1]
		todo = todo[:len(todo)-1]

		prepareFunc(next)
		todo = append(todo, next.Closures...)
	}

	if base.Errors() > errorsBefore {
		return
	}

	// 排队就是为了自己。编译函数将处理
	// 在完成后安排其闭包的编译。
	compilequeue = append(compilequeue, fn)
}

// prepareFunc处理任何剩余的前端编译任务
// 还不能安全地同时执行。
func prepareFunc(fn *ir.Func) {
	// 尽早设置函数的LSym以避免与汇编程序的数据争用。
	// 在漫游之前执行此操作，因为漫游需要LSym来设置属性/重新定位
	// （例如，在MarkTypeUsedInInterface中）。
	ssagen.InitLSym(fn, true)

	// 计算参数偏移。
	types.CalcSize(fn.Type())

	typecheck.DeclContext = ir.PAUTO
	ir.CurFunc = fn
	walk.Walk(fn)
	ir.CurFunc = nil // 强制不再使用CurFunc
	typecheck.DeclContext = ir.PEXTERN
}

// compileFunctions编译compilequeue中的所有函数。
// 它把工人分散出去做这项工作
// 并等待它们完成。
func compileFunctions() {
	if len(compilequeue) == 0 {
		return
	}

	if race.Enabled {
		// 随机化编译顺序，尝试摆脱种族。
		tmp := make([]*ir.Func, len(compilequeue))
		perm := rand.Perm(len(compilequeue))
		for i, v := range perm {
			tmp[v] = compilequeue[i]
		}
		copy(compilequeue, tmp)
	} else {
		// 首先编译最长的函数，
		// 因为他们很可能是最慢的。
		// 这有助于避免掉队。
		sort.Slice(compilequeue, func(i, j int) bool {
			return len(compilequeue[i].Body) > len(compilequeue[j].Body)
		})
	}

	// 默认情况下，我们立即在当前goroutine上执行工作
	// 作为独行侠。
	queue := func(work func(int)) {
		work(0)
	}

	if nWorkers := base.Flag.LowerC; nWorkers > 1 {
		// 对于并发构建，我们为每个任务创建一个goroutine，但是
		// 要求他们在执行工作时持有唯一的工作人员ID
		// 限制并行性。
		workerIDs := make(chan int, nWorkers)
		for i := 0; i < nWorkers; i++ {
			workerIDs <- i
		}

		queue = func(work func(int)) {
			go func() {
				worker := <-workerIDs
				work(worker)
				workerIDs <- worker
			}()
		}
	}

	var wg sync.WaitGroup
	var compile func([]*ir.Func)
	compile = func(fns []*ir.Func) {
		wg.Add(len(fns))
		for _, fn := range fns {
			fn := fn
			queue(func(worker int) {
				ssagen.Compile(fn, worker)
				compile(fn.Closures)
				wg.Done()
			})
		}
	}

	types.CalcSizeDisabled = true // 同时计算尺寸不安全
	base.Ctxt.InParallel = true

	compile(compilequeue)
	compilequeue = nil
	wg.Wait()

	base.Ctxt.InParallel = false
	types.CalcSizeDisabled = false
}
